package ray;

import java.util.ArrayList;
import ray.math.Color;
import ray.math.Point3;
import ray.math.Ray;
import ray.shader.Shader;
import ray.surface.Surface;


/**
 * The scene is just a collection of objects that compose a scene. The camera,
 * the list of lights, and the list of surfaces.
 *
 * @author ags
 */
public class Scene {
	
	/** The camera for this scene. */
	protected Camera camera;
	public void setCamera(Camera camera) { this.camera = camera; }
	public Camera getCamera() { return this.camera; }
	
	/** The list of lights for the scene. */
	protected ArrayList<Light> lights = new ArrayList<Light>();
	public void addLight(Light toAdd) { lights.add(toAdd); }
	public ArrayList<Light> getLights() { return this.lights; }
	
	/** The list of surfaces for the scene. */
	protected ArrayList<Surface> surfaces = new ArrayList<Surface>();
	public void addSurface(Surface toAdd) { surfaces.add(toAdd); }
	
	/** The list of materials in the scene . */
	protected ArrayList<Shader> shaders = new ArrayList<Shader>();
	public void addShader(Shader toAdd) { shaders.add(toAdd); }
	
	/** Image to be produced by the renderer **/
	protected Image outputImage;
	public Image getImage() { return this.outputImage; }
	public void setImage(Image outputImage) { this.outputImage = outputImage; }
	
	public Color calcColor(Ray ray) {
		Color color = new Color(0,0,0);
		
		double minT = Double.POSITIVE_INFINITY;
		Surface closestSurface = null;
		
		for(int i = 0;i<surfaces.size();i++){
			Surface surface = (Surface) surfaces.get(i);
			double intersectT = surface.rayCollision(ray);
			
			if(intersectT > 0.0){
				if(intersectT<minT){
					minT = intersectT;
					closestSurface = surface;
				}
			}
		}
		
		if(minT<Double.POSITIVE_INFINITY){
			Point3 point = ray.calcPoint(minT);
			for(int l = 0;l<lights.size();l++){
				if(!isInShadow(point,(Light) lights.get(l),closestSurface)){
					color.add(closestSurface.getShader().shade(ray,point,closestSurface.calcUnitNormal(point),(Light)lights.get(l)));
				}
			}
		}
		return color;
	}
	
	private boolean isInShadow(Point3 point, Light light, Surface surfaceMask) {
		Ray shadowRay = new Ray();
		shadowRay.calc(point, light.position);
		
		for(int i = 0;i<surfaces.size();i++){
			Surface surface = (Surface) surfaces.get(i);
			double intersectT = 0.0;
			if(surface != surfaceMask){
				intersectT = surface.rayCollision(shadowRay);
			}
			if(intersectT > 0.0){
				return true;
			}
		}
		
		return false;
	}

}
